iT邦幫忙

2026 iThome 鐵人賽

DAY 19
0
AI Engineering

30 天打造我的 AI 開發工作流:從需求分析到上線系列 第 19

Day 19|寫在 CLAUDE.md 裡的架構規則,擋得住什麼?

  • 分享至 

  • xImage
  •  

前言

CLAUDE.md 裡有一行「分層是 api → services → repositories」。我照著跑完一個工作單元,產出的是四個目錄——而規則沒有攔住我,最後是規則被改掉。


昨天說到

昨天把開發流程改成測試先寫,也真的跑完了一輪,測試可以幫我確認一件事:**功能有沒有照規格運作。**例如「確認人按下確認後,版本的確認狀態要更新」,測試可以直接驗證結果對不對。

但假設有人把查資料、業務判斷全部寫進 api/,功能沒壞,測試也沒紅,但程式已經開始偏離架構,這就是昨天測試沒有處理到的另一種問題:

測試可以驗證「事情有沒有做對」,但不一定知道「這件事是不是放在對的地方」。

那架構規則要靠什麼守?


原本的結構

專案建起來的時候,/init 掃過程式碼,把後端分成三層:

**Layering intent** (backend): `api/` routes → `services/` business logic
→ `repositories/` data access.

實作完後回頭看產出的目錄:

app/api/          app/services/          app/repositories/
app/models/       app/schemas/    ← 這個不在那條鏈上

schemas/

schemas/ 它裝的是對外的請求與回應模型,不屬於 api/services/repositories/ 這三層。

因此 claude 實作時自己決定產出四層,CLAUDE.md 也被改成四層。

**`app/schemas/` is a fourth directory, deliberately outside that chain.**

這段是跟實作同一筆 commit 進去的——不是事後檢討才補的,是實作當下順手改的。

services/errors.py

產出的內容 services/errors.py 裡的例外類別帶著 HTTP status code

class ConflictError(DomainError):
    status_code = 409

HTTP 狀態碼是傳輸層的概念,出現在 service 層算是跨層。純粹一點的做法是 service 只丟領域錯誤,由 api/ 那層決定對應哪個碼。我沒那樣做——換來的是所有錯誤在 main.py 一個地方渲染成同一個形狀,前端只需要處理一種錯誤結構。

兩個例外,差別只在有沒有被記錄

是什麼 理由寫在哪 什麼時候寫的
schemas/ 目錄 一個不在鏈上的目錄 CLAUDE.md 事後,跟實作同一筆 commit
errors.py 帶狀態碼 一個跨層的欄位 檔案開頭的 docstring 當下

怎麼找到例外

抓到例外的是plan.md 裡的檔案清單。

規劃時列了 36 個檔案,實作完變成 41 個。因為清單在,多出來的每一個都得解釋

檔案 為什麼原本沒列
schemas/user.py UserRead 同時被三份 schema 用到。塞進其中任何一份,都會讓另外兩份反向 import
repositories/user_repository.py 原本想把 user 查詢塞進會議的 repository。但每個請求都要查 user,掛在會議底下不合理
services/errors.py 契約裡的錯誤碼約定需要一個載體。塞進狀態機那個檔案,會讓它同時是狀態機和錯誤型別定義檔
services/user_service.py 分層是 api → services → repositories。少了它,api/ 就得直接呼叫 repository——在自己的規則上開一個例外
tests/test_concurrency.py 並行測試需要兩條真實連線,不能用其他測試那套交易回滾的隔離方式

這五行理由記錄的是規劃時想不到、實作當下才浮現的結構壓力。

規則是文字,清單則是可以逐項核對的東西。


grep

清單要人去對。

有些規則可以變成機器問得出來的問題,像是「分層」規則拆開來,其實是三句可以檢查的話:

規則 檢查方式 實測
router 不做判斷 api/ 裡有幾個 if 0
repository 不做判斷 repositories/ 裡有幾個 raise 0
service 不碰傳輸層 services/ 有沒有 from fastapi 沒有

這跟前面幾天是同一件事:能被落實的規則,是那些寫得成檢查的。只是這次檢查的不是業務規則,是結構。


結論

1. 架構規則不會擋住任何人

CLAUDE.md 是 context,不是強制配置。它會被讀到、大部分時候會被遵守,但它沒有「拒絕」這個動作。以為寫進去就安全了,是把提示當成了閘門。

2. 形式有強弱之分

一句話的規則      →  要讀完整個目錄才知道有沒有違反
一份檔案清單      →  逐項核對,多出來的要解釋
一個 grep 得出的數字 →  30 秒回答得出來

越往下越強,但也越窄——status_code = 409 就是證據:grep 回報乾淨,跨層的東西還是在那裡。

文字負責描述意圖,清單負責核對變化,機器檢查負責守住可以形式化的邊界。
所以三種都需要,不是挑一個。

3. 例外不可怕,沒被記錄的例外才可怕

這一輪有兩個例外:多開 schemas/ 目錄、errors.py 帶狀態碼。兩個我都認為是對的決定。

差別在於後者的理由寫在程式碼裡、當下就寫了;前者是事後才補進規範的。而事後補的那種,下一個人只會看到一個「本來就是四層」的規則,看不到它曾經是三層、也看不到誰決定要多一層

明天:資料庫與 Alembic。


上一篇
DAY18 | TDD × AI:測試就是可執行的規格
下一篇
Day 20|資料庫與 Alembic:migration 的人工 review
系列文
30 天打造我的 AI 開發工作流:從需求分析到上線22
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言